//#include "stdafx.h"
#include "MainLogic.h"

ifstream filename ("testinput.txt");

const string MainLogic:: invalidCommand="The command entered is invalid.";
const string MainLogic:: successMessage="Operation was Successful.";
const string MainLogic:: errorShowCommand ="No events to display.";

ofstream logger("logfile.txt");

Date MainLogic::getCurrentSystemDate()
{
	logger << "Getting Current Date" << endl;
	time_t t=time(0);
	tm* now=localtime(&t);
	Date obj;
	obj.setDay(now->tm_mday);
	obj.setMonth(now->tm_mon+1);
	obj.setYear(now->tm_year + 1900);
	return obj;
}

MainLogic::MainLogic()
{
	Date obj=getCurrentSystemDate();
	currentList.push_back(obj);
	historyList.push_back(obj);
	FileStorage inputFile;
	logger << "Reading input File" << endl;
	inputFile.readFromFile(currentList, historyList);

	updateDisplayList();
	updatePendingList();
	//dtype=PENDING;
}


MainLogic::~MainLogic()
{
	FileStorage outputFile;
	logger << "Writing to file" << endl;
	logger.close();
	outputFile.writeToFile(currentList, historyList);
}

COMMANDTYPE MainLogic:: getCommandType(string firstWord)
{
	assert(firstWord!="");
	int i = 0;
	if (equalsIgnoreCase(firstWord,"add"))
		i = 1;
	else if (equalsIgnoreCase(firstWord,"show"))
		i = 2;
	else if (equalsIgnoreCase(firstWord,"delete"))
		i = 3;
	else if (equalsIgnoreCase(firstWord,"edit"))
		i = 4;
	else if (equalsIgnoreCase(firstWord,"date"))
		i = 5;
	else if (equalsIgnoreCase(firstWord,"location"))
		i = 6;
	else if (equalsIgnoreCase(firstWord,"time"))
		i=7;
	else if (equalsIgnoreCase(firstWord,"undo"))
		i=8;
	else if(equalsIgnoreCase(firstWord,"sft"))
		i=9;
	else if (equalsIgnoreCase(firstWord,"by"))
		i=10;
	else if(equalsIgnoreCase(firstWord,"done"))
		i=11;
	else if (equalsIgnoreCase(firstWord,"pending"))
		i=12;
	else if (equalsIgnoreCase(firstWord,"history"))
		i=13;
	else
		i=14;
	return COMMANDTYPE(i);
}

bool MainLogic::equalsIgnoreCase(string str1, string str2){
	
	if (str1.size() != str2.size()) 
		return false;
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2){
        if (tolower(*c1) != tolower(*c2)) 
			return false;
    }
    return true;
}


string MainLogic::toLower(string am_pm)
{
	stringstream converted;
	for (int i=0; i<am_pm.size();i++)
	{
		if (isdigit(am_pm[i]))
			converted << am_pm[i];
		else
			converted << (char)tolower(am_pm[i]);
	}
	return converted.str();
}

string MainLogic::updateIfTodayOrTomorrow(string formattedCommand)
{
	string temp;
	int i;
	string wordToFind = toLower(formattedCommand);
	int posToday= wordToFind.find(" today");
	int posTomorrow=wordToFind.find(" tomorrow");
	int posMonday=wordToFind.find(" monday");
	int posTuesday=wordToFind.find(" tuesday");
	int posWednesday=wordToFind.find(" wednesday");
	int posThursday=wordToFind.find(" thursday");
	int posFriday=wordToFind.find(" friday");
	int posSaturday=wordToFind.find(" saturday");
	int posSunday=wordToFind.find(" sunday");
	
	if (posToday !=-1)
		i =posToday;
	else if (posTomorrow!=-1)
		i = posTomorrow;
	else if (posTomorrow!=-1)
		i = posTomorrow;
	else if (posMonday!=-1)
		i = posMonday;
	else if (posTuesday!=-1)
		i = posTuesday;
	else if (posWednesday!=-1)
		i = posWednesday;
	else if (posThursday!=-1)
		i = posThursday;
	else if (posFriday!=-1)
		i = posFriday;
	else if (posSaturday!=-1)
		i = posSaturday;
	else
		i = posSunday;

	formattedCommand.insert(i+1, "date ");
	return formattedCommand;
}

bool MainLogic:: hasTodayOrTomorrowAsCommand(string formattedCommand)
{
	string wordToFind = toLower(formattedCommand);
	int posToday= wordToFind.find(" today");
	int posTomorrow=wordToFind.find(" tomorrow");
	int posMonday=wordToFind.find(" monday");
	int posTuesday=wordToFind.find(" tuesday");
	int posWednesday=wordToFind.find(" wednesday");
	int posThursday=wordToFind.find(" thursday");
	int posFriday=wordToFind.find(" friday");
	int posSaturday=wordToFind.find(" saturday");
	int posSunday=wordToFind.find(" sunday");

	if (posToday!=-1 || posTomorrow!=-1 ||posMonday!=-1||posTuesday!=-1||posWednesday!=-1||posThursday!=-1||posFriday!=-1||posSaturday!=-1||posSunday!=-1)
		return true;
	else
		return false;
}

bool MainLogic:: hasDeadlineWithTime(string command)
{
	int posTime = -1;
	string wordToFind = toLower (command);
	int posBy = wordToFind.find(" by");
	if (posBy!=-1)
	{
		posTime = wordToFind.find(" time");
	}
	if (posTime!=-1)
		return true;
	else
		return false;
}

string MainLogic:: updateWithTimeRemoved(string command)
{
	string wordToFind = toLower (command);
	int posTime = wordToFind.find(" time");
	assert(posTime!=-1);
	command = command.erase(posTime,5);
	return command;
}

void MainLogic ::processCommand( string command )
{
	struct Task currentTask;

	string firstWord, formattedCommand;
	COMMANDTYPE type;
	string objdetails;

	firstWord = getFirstWord (command);
	type = getCommandType (firstWord);
	if (hasTodayOrTomorrowAsCommand(command))
		command=updateIfTodayOrTomorrow(command);
	if(!isCommand(firstWord))
	{
		type = ADD;
		formattedCommand=command;
	} 
	else
		formattedCommand=removeFirstWord(command);

	processTask(formattedCommand, type, currentTask);//tokenize the statement and set it into its details
	
	switch (type)
	{
		case ADD:
			logger << "Adding" << endl;
			objdetails = processEvents.getEventDetailsId(currentList, displayList, currentTask);
			if (processEvents.addEvent(currentList, currentTask))
				statusMessage=successMessage+ objdetails;// + statusMessage;
			else
				statusMessage=invalidCommand;// + statusMessage;
			updateDisplayList();
			break;
		case DELETE:
			if(dtype!=PENDING)
			{
				logger << "Deleting" << endl;
				objdetails = processEvents.getEventDetailsId(currentList, displayList, currentTask);
				if (processEvents.deleteEvent(currentList, displayList, currentTask))
				{
					updateDisplayList();
					statusMessage=successMessage + objdetails;
				}
				else
					statusMessage=invalidCommand;
			}
			else
				statusMessage=invalidCommand;
			break;
		case EDIT:
			logger << "Editing" << endl;
			if(dtype==UPCOMING)
			{
				objdetails = processEvents.getEventDetailsId(currentList, displayList, currentTask);
				if (processEvents.editEvent(currentList,displayList, currentTask))
				{
					updateDisplayList();
					statusMessage=successMessage + objdetails;
				}
				else
					statusMessage=invalidCommand;
			}
			else if(dtype==PENDING)
			{
				if(processHistory.editEvent(historyList,pendingList,currentList,currentTask))
				{
					updateDisplayList();
					statusMessage=successMessage;
				}
				else
				{
					statusMessage=invalidCommand;
				}
			}
			break;
		case SHOW:
			logger << "Showing" << endl;
			if (formattedCommand=="")
				updateDisplayList();
			
			else if (!isShowParameterValid(currentTask))
				statusMessage = invalidCommand;
			else
			{
				if (dtype==PENDING)
				{
					if (processHistory.showEvents(historyList, pendingList, currentTask))
					{
						statusMessage="";
					}
					if (pendingList.empty())
					{
						processHistory.showHistory(historyList, pendingList);
						statusMessage = errorShowCommand;
					}
				}
				else
				{
					if (processEvents.showEvents(currentList, displayList, currentTask))
						statusMessage="";
						
					if (displayList.empty())
					{
						updateDisplayList();
						statusMessage = errorShowCommand;
					}
				}
			}
			break;

		case SFT:
			logger << "Showing Free Time" << endl;
			outputDisplay=processEvents.showFreeTime(currentList,displayList,currentTask);
			dtype=OTHER;
						
			break;

		case UNDO:
			logger << "Undoing" << endl;
			statusMessage=processEvents.undoCommand(currentList);
			updateDisplayList();
			break;
		
		case DONE:
			logger << "Marking as done" << endl;
			if(dtype==UPCOMING)
			{
				if(processEvents.markAsDone(currentList,displayList,currentTask))
					statusMessage=successMessage;
				else
					statusMessage=invalidCommand;
				updateDisplayList();
			}
			else if(dtype==PENDING)
			{
				if(processHistory.markAsDone(historyList,pendingList,currentTask))
					statusMessage=successMessage;
				else
					statusMessage=invalidCommand;
				updatePendingList();
			}
			break;

		case COMMANDTYPE::PENDING:
			updatePendingList();
			break;
		
		case HISTORY:
			dtype=PENDING;
			processHistory.showHistory(historyList, pendingList);
			break;
		
		default:
			statusMessage=invalidCommand;
			break;
	}

	FileStorage outputFile;
	logger << "Writing to file" << endl;
	outputFile.writeToFile(currentList, historyList);
}

bool MainLogic::isShowParameterValid(struct Task currentTask)
{
	int count=0;
	if (currentTask.name!="")
		count++;
	if (currentTask.date!="")
		count++;
	if (currentTask.location!="")
		count++;
	if (currentTask.time!="")
		count++;
	if (count!=1 && count!=0)
		return false;
	return true;
}

bool MainLogic:: isCommand(string word)
{
	bool flag=false;
	if (getCommandType(word)!=INVALID)
		flag=true;
	return flag;
}

string MainLogic:: getFirstWord(string command)
{
	stringstream strstr;
	string firstWord;
	strstr << command;
	strstr >> firstWord;
	return firstWord;
}

string MainLogic ::removeFirstWord(string  command)
{
	string buffer;
	string statementWithFirstWordRemoved=" ";
	stringstream formattedCommand(command);
	formattedCommand >> buffer;
	while(formattedCommand >> buffer)
	{
		statementWithFirstWordRemoved+=buffer+" ";
	}
	statementWithFirstWordRemoved=trim(statementWithFirstWordRemoved);
	return statementWithFirstWordRemoved;
}

string MainLogic::trim(string text){
	
	string trimmedText;
	int startPos=text.find_first_not_of(' ');
	int endPos=text.find_last_not_of(' ');
	if(startPos==-1)
		trimmedText="";
	else
		trimmedText=text.substr(startPos,endPos-startPos+1);
	return trimmedText;
}

vector <string >MainLogic :: tokenizeCommand (string formattedCommand)
{
	istringstream commandIn(formattedCommand);
	string buffer;
	vector<string> tokenizedList;
	while ( commandIn >> buffer )
	{
		tokenizedList.push_back(buffer);
	}
	return tokenizedList;
}

void MainLogic ::processTask(string formattedCommand, COMMANDTYPE type, struct Task & currentTask)
{
	logger << "Processing Details" << endl;
	assert(type!=INVALID);
	int i=0;
	string currentWord;
	string detail;
	COMMANDTYPE lastCommand=type;
	vector <string> tokenizedList;
	tokenizedList=tokenizeCommand (formattedCommand);
	ostringstream detailOut;

	for (i=0; i < tokenizedList.size();i++)
	{
		currentWord = tokenizedList.at(i);
		assert(currentWord!="");
		if (!isCommand (currentWord))
		{
			if (currentWord[0]!='/')
				detailOut << currentWord << " ";
			else
				detailOut << currentWord.substr(1) << " ";

			if (i==tokenizedList.size()-1)
			{
				detail=trim(detailOut.str());
				setDetails (lastCommand, detail, currentTask);
				detailOut.str("");
			}
		}
		else
		{
			if (detailOut.str()!="")
			{
				detail=trim(detailOut.str());
				setDetails (lastCommand, detail, currentTask);
				detailOut.str("");
			}
			lastCommand = getCommandType(currentWord);
		}
	}
	
}

void MainLogic::setDetails (COMMANDTYPE commandType, string detail, struct Task &currentTask)
{
	if (commandType == ADD || commandType == SHOW)
	{
		if (!detail.empty())
			currentTask.name = detail;
	}
	else if (commandType==DAY)
	{
		if (!detail.empty())
			currentTask.date = detail;
	}
	else if (commandType==LOCATION)
	{
		if (!detail.empty())
			currentTask.location = detail;
	}
	else if (commandType==DELETE)
	{
		if (!detail.empty())
			currentTask.index = detail;
	}
	else if (commandType==DONE)
	{
		if (!detail.empty())	
			currentTask.index = detail;
	}
	else if (commandType==TIME|| commandType==BY)
	{
		if (!detail.empty())
			currentTask.time = detail;
		if (commandType==BY)
			currentTask.deadline = "d";
	}
	if (commandType==EDIT )
	{
		if (!detail.empty())
		{
			currentTask.index = getFirstWord(detail);
			detail = removeFirstWord(detail);
			//detail=trim(detail);
			if (!detail.empty())			
				currentTask.name = detail;
		}
	}
}

void MainLogic::updateDisplayList()
{
	logger << "Updating Display List" << endl;
	int j =0, k=0;
	displayList.clear();
	for (int j =0; j<currentList.size(); j++)
	{
		for (int k = 0; k< currentList[j].getEventListLength(); k++)
		{
			displayList.push_back(currentList[j].getEvent(k));
		}
	}
	dtype=UPCOMING ;
}

void MainLogic::updatePendingList()
{
	logger << "Updating Pending List" << endl;
	int j =0, k=0;
	pendingList.clear();
	for (int j =0; j<historyList.size(); j++)
	{
		for (int k = 0; k< historyList[j].getEventListLength(); k++)
		{
			if(historyList[j].getEvent(k).getEventStatus()=="nd")
				pendingList.push_back(historyList[j].getEvent(k));
		}
	}
	if (pendingList.size()==0)
	{
		updateDisplayList();
		dtype=UPCOMING;
	}
	else 
		dtype = PENDING;
}
string MainLogic::formatDisplayOutput()
{
	vector<Event> list;
	Event currentEvent;
	string displayDate="";
	string prevDate="";
	stringstream output;
	Date tempDate;

	if(dtype==PENDING)
		list=pendingList;
	else if(dtype==UPCOMING)
		list=displayList;

	for (int i =0; i<list.size(); i++)
	{
		currentEvent = list[i];
		displayDate = currentEvent.getId().substr(0,8);
		
		try{
			displayDate = tempDate.getConvertedDate(displayDate);
		}
		catch(string errorMessage)
		{
			output << errorMessage << endl;
		}

		//Date dateObj = temp.dateStringToObject(displayDate, getCurrentSystemDate());
		//displayDate = dateObj.getConvertedDate();
		if (displayDate!=prevDate)
		{
			prevDate=displayDate;
			output <<"\t\t\t" << displayDate << endl;
		}
		output << currentEvent.getId() << "  ";
		output << i+1 << ". ";
		if (currentEvent.getEventType()=="d")
			output << "[DEADLINE] ";
		output << currentEvent.getName() ;
		if (currentEvent.getTime()!="")
		{
			if (currentEvent.getEventType()=="d")
				output << " by "<< currentEvent.getTime();
			else
				output << " at "<< currentEvent.getTime();
		}
		if (currentEvent.getLocation()!="")
			output << " at "<< currentEvent.getLocation();
		output<<" " << currentEvent.getEventStatus();
				//if (i+1<displayList.size())
		output << endl;	
	}

	return output.str();
}
string MainLogic::displayOutput()
{
	logger << "Formatting Display output" << endl;
	//int i;
	stringstream output;
	/*Event currentEvent;
	string displayDate="";
	string prevDate="";
	ProcessList temp;
	Date tempDate;*/

	//if(dtype==PENDING)
	//{

	//	for (i =0; i<pendingList.size(); i++)
	//	{
	//		currentEvent = pendingList[i];
	//		displayDate = currentEvent.getId().substr(0,8);

	//		try{
	//			displayDate = tempDate.getConvertedDate(displayDate);
	//		}
	//		catch(string errorMessage)
	//		{
	//			output << errorMessage << endl;
	//		}

	//		//Date dateObj = temp.dateStringToObject(displayDate, getCurrentSystemDate());
	//		//displayDate = dateObj.getConvertedDate();
	//		if (displayDate!=prevDate)
	//		{
	//			prevDate=displayDate;
	//			output <<"\t\t\t" << displayDate << endl;
	//		}

	//		output << currentEvent.getId() << "  ";
	//		output << i+1 << ". ";
	//		if (currentEvent.getEventType()=="d")
	//			output << "[DEADLINE] ";
	//		output << currentEvent.getName() ;
	//		if (currentEvent.getTime()!="")
	//		{
	//			if (currentEvent.getEventType()=="d")
	//				output << " by "<< currentEvent.getTime();
	//			else
	//				output << " at "<< currentEvent.getTime();
	//		}
	//		if (currentEvent.getLocation()!="")
	//			output << " at "<< currentEvent.getLocation();
	//		output<<" " << currentEvent.getEventStatus();
	//
	//		//if (i+1<displayList.size())
	//			output << endl;	
	//	}
	//}

	//else if(dtype==UPCOMING)
	//{

	//	for (i =0; i<displayList.size(); i++)
	//	{
	//		currentEvent = displayList[i];
	//		displayDate = currentEvent.getId().substr(0,8);

	//		try{
	//			displayDate = tempDate.getConvertedDate(displayDate);
	//		}
	//		catch(string errorMessage)
	//		{
	//			output << errorMessage << endl;
	//		}

	//		//Date dateObj = temp.dateStringToObject(displayDate, getCurrentSystemDate());
	//		//displayDate = dateObj.getConvertedDate();
	//		if (displayDate!=prevDate)
	//		{
	//			prevDate=displayDate;
	//			output <<"\t\t\t" << displayDate << endl;
	//		}

	//		output << currentEvent.getId() << "  ";
	//		output << i+1 << ". ";
	//		if (currentEvent.getEventType()=="d")
	//			output << "[DEADLINE] ";
	//		output << currentEvent.getName() ;
	//		if (currentEvent.getTime()!="")
	//		{
	//			if (currentEvent.getEventType()=="d")
	//				output << " by "<< currentEvent.getTime();
	//			else
	//				output << " at "<< currentEvent.getTime();
	//		}
	//		if (currentEvent.getLocation()!="")
	//			output << " at "<< currentEvent.getLocation();
	//		output<<" " << currentEvent.getEventStatus();
	//		//if (i+1<displayList.size())
	//			output << endl;	
	//	}
	//}
	if(dtype!=OTHER)
		output << formatDisplayOutput();
	else
	{
		output << outputDisplay << endl;
		outputDisplay="";
	}
	return output.str();
}

/*string MainLogic::getConvertedDate(string tempDate)
{
	assert(tempDate!="");
	stringstream convertedDate ("");
	int day = atoi(tempDate.substr(0,2).c_str());//(tempDate.substr(0,2));
	if(day==0)
		throw "Date Not Valid";
	int month = atoi (tempDate.substr(2,2).c_str());
	if(month==0)
		throw "Month Not Valid";
	int year = atoi(tempDate.substr(4,4).c_str());
	if(year==0)
		throw "Year Not Valid";

	convertedDate<<day;
	if (day%10==1)
		convertedDate<<"st ";
	else if (day%10==2)
		convertedDate<<"nd ";
	else if (day%10==3)
		convertedDate<<"rd ";
	else 
		convertedDate<<"th ";

	string months[]={"January","February","March","April","May","June","July","August","September","October","November","December"};
	convertedDate<<months[month-1];
	convertedDate<<", ";
	convertedDate<<year;

	return convertedDate.str();
}*/

string MainLogic::getStatusMessage()
{
	return statusMessage;
}

int main()
{
	MainLogic test;
	cout << test.displayOutput();
	string input;
	//ifstream ifs("testinput.txt");
	//ofstream ofs("expected.txt");
	while(getline(cin,input)){
		test.processCommand(input);
		logger << "Displaying" << endl;
		cout << test.displayOutput();
		cout << test.getStatusMessage() << endl <<endl;
	}
	//ofs.close();
	//ifs.close();
	return 0;
}


